use serde::{Deserialize, Serialize};
use std::sync::Arc;
use std::fmt::Debug;

/// Defines serialization formats available in the system.
#[derive(Clone, Debug)]
pub enum SerializationFormat {
    /// MessagePack serialization format.
    MessagePack,
    /// JSON serialization format.
    #[cfg(feature = "json")]
    Json,
    /// Protocol Buffers serialization format.
    #[cfg(feature = "protobuf")]
    Protobuf,
}

impl SerializationFormat {
    /// The name of the serialization format.
    pub fn name(&self) -> &'static str {
        match self {
            SerializationFormat::MessagePack => "messagepack",
            #[cfg(feature = "json")]
            SerializationFormat::Json => "json",
            #[cfg(feature = "protobuf")]
            SerializationFormat::Protobuf => "protobuf",
        }
    }
    
    /// Serialize a value to bytes.
    pub fn serialize_raw(&self, value: &dyn erased_serde::Serialize) -> Result<Vec<u8>, String> {
        match self {
            SerializationFormat::MessagePack => rmp_serde::to_vec_named(value).map_err(|e| e.to_string()),
            #[cfg(feature = "json")]
            SerializationFormat::Json => serde_json::to_vec(value).map_err(|e| e.to_string()),
            #[cfg(feature = "protobuf")]
            SerializationFormat::Protobuf => Err("Direct Protobuf serialization requires special handling for each type.".to_string()),
        }
    }
    
    /// Deserialize bytes directly to type T.
    pub fn deserialize_any<'de, T: Deserialize<'de>>(&self, bytes: &'de [u8]) -> Result<T, String> {
        match self {
            SerializationFormat::MessagePack => rmp_serde::from_slice(bytes).map_err(|e| e.to_string()),
            #[cfg(feature = "json")]
            SerializationFormat::Json => serde_json::from_slice(bytes).map_err(|e| e.to_string()),
            #[cfg(feature = "protobuf")]
            SerializationFormat::Protobuf => Err("Direct Protobuf deserialization requires special handling.".to_string()),
        }
    }
}

/// Helper function to serialize a value using a specific format.
pub fn serialize_value<T: Serialize>(format: &SerializationFormat, value: &T) -> Result<Vec<u8>, String> {
    let erased = &value as &dyn erased_serde::Serialize;
    format.serialize_raw(erased)
}

/// Helper function to deserialize a value using a specific format.
pub fn deserialize_value<'de, T: Deserialize<'de>>(format: &SerializationFormat, bytes: &'de [u8]) -> Result<T, String> {
    format.deserialize_any(bytes)
}

/// Helper trait for types that can be serialized to Protocol Buffers format.
#[cfg(feature = "protobuf")]
pub trait ProtobufSerialize {
    /// Serialize to Protocol Buffers format.
    fn to_protobuf(&self) -> Result<Vec<u8>, String>;
}

/// Helper trait for types that can be deserialized from Protocol Buffers format.
#[cfg(feature = "protobuf")]
pub trait ProtobufDeserialize<T> {
    /// Deserialize from Protocol Buffers format.
    fn from_protobuf(bytes: &[u8]) -> Result<T, String>;
}

// Implementation for types that implement prost::Message
#[cfg(feature = "protobuf")]
impl<T: prost::Message> ProtobufSerialize for T {
    fn to_protobuf(&self) -> Result<Vec<u8>, String> {
        let mut buf = Vec::new();
        prost::Message::encode(self, &mut buf)
            .map_err(|e| format!("Failed to encode protobuf: {}", e))?;
        Ok(buf)
    }
}

#[cfg(feature = "protobuf")]
impl<T: prost::Message + Default> ProtobufDeserialize<T> for T {
    fn from_protobuf(bytes: &[u8]) -> Result<T, String> {
        prost::Message::decode(bytes)
            .map_err(|e| format!("Failed to decode protobuf: {}", e))
    }
}

/// Configuration for serialization formats in the actor system.
#[derive(Clone, Debug)]
pub struct SerializationConfig {
    /// The default serialization format to use.
    format: SerializationFormat,
}

impl Default for SerializationConfig {
    fn default() -> Self {
        Self {
            format: SerializationFormat::MessagePack,
        }
    }
}

impl SerializationConfig {
    /// Create a new serialization configuration with the default format (MessagePack).
    pub fn new() -> Self {
        Self::default()
    }
    
    /// Set the serialization format to use.
    pub fn with_format(mut self, format: SerializationFormat) -> Self {
        self.format = format;
        self
    }
    
    /// Get the current serialization format.
    pub fn format(&self) -> &SerializationFormat {
        &self.format
    }
}

// Global serialization configuration
static mut SERIALIZATION_CONFIG: Option<SerializationConfig> = None;

/// Initialize the global serialization configuration.
///
/// This function should be called before bootstrapping the actor system.
/// If not called, the default configuration will be used.
pub fn init_serialization(config: SerializationConfig) {
    unsafe {
        SERIALIZATION_CONFIG = Some(config);
    }
}

/// Get the global serialization configuration.
///
/// If not initialized, the default configuration will be returned.
pub fn get_serialization_config() -> &'static SerializationConfig {
    unsafe {
        SERIALIZATION_CONFIG.get_or_insert_with(SerializationConfig::default)
    }
}

/// Serialize a value using the configured serialization format.
pub fn serialize<T: Serialize>(value: &T) -> Result<Vec<u8>, String> {
    let format = get_serialization_config().format();
    let erased = &value as &dyn erased_serde::Serialize;
    format.serialize_raw(erased)
}

/// Deserialize a value using the configured serialization format.
pub fn deserialize<'de, T: Deserialize<'de>>(bytes: &'de [u8]) -> Result<T, String> {
    let format = get_serialization_config().format();
    format.deserialize_any(bytes)
} 